import { GameRoleCtrl } from './../../game/GameCtrl';
import { GameMapItemType } from './../../game/GameDefine';
import GameMapModel from "../../game/model/GameMapModel";
import GameRoleModel from "../../game/model/GameRoleModel";
import BaseWidget from "../BaseWidget";
import GameMapItemDoorWidget from "./GameMapItemDoorWidget";
import GameMapItemMonsterWidget from "./GameMapItemMonsterWidget";
import GameMapItemWidget from "./GameMapItemWidget";
import GameRoleWidget from "./GameRoleWidget";
import OrientationWidget from '../OrientationWidget';

const {ccclass, property, menu} = cc._decorator;

@ccclass
@menu("Widget/Game/GameMapWidget")
export default class GameMapWidget extends OrientationWidget {
    @property(cc.Node)
    brickLayer: cc.Node = null;
    @property(cc.Node)
    itemLayer: cc.Node = null;
    @property(cc.Node)
    effectLayer: cc.Node = null;

    @property([cc.Prefab])
    prefab_items: cc.Prefab[] = [];
    @property(cc.Prefab)
    prefab_brick: cc.Prefab = null;
    @property(cc.Prefab)
    prefab_brick_conner: cc.Prefab = null;
    @property(cc.Prefab)
    prefab_brick_frame: cc.Prefab = null;

    @property(cc.Prefab)
    prefab_effect_attack: cc.Prefab = null;
    @property(cc.Prefab)
    prefab_effect_dead: cc.Prefab = null;

    @property(GameRoleWidget)
    roleWidget: GameRoleWidget = null;

    private _itemWidth = 68;
    private _itemHeight = 68;

    private _itemPrefabsMapping: cc.Prefab[] = [];
    private _itemNodes: cc.Node[][] = [];
    private _brickNodes: cc.Node[][] = [];

    protected onLoad(): void {
        super.onLoad();

        this._itemPrefabsMapping[GameMapItemType.WALL] = this.prefab_items[0];
        this._itemPrefabsMapping[GameMapItemType.EXIT] = this.prefab_items[1];
        this._itemPrefabsMapping[GameMapItemType.DOOR_H] = this.prefab_items[2];
        this._itemPrefabsMapping[GameMapItemType.DOOR_V] = this.prefab_items[2];
        this._itemPrefabsMapping[GameMapItemType.WEAPON_1] = this.prefab_items[3];
        this._itemPrefabsMapping[GameMapItemType.WEAPON_2] = this.prefab_items[4];
        this._itemPrefabsMapping[GameMapItemType.WEAPON_3] = this.prefab_items[5];
        this._itemPrefabsMapping[GameMapItemType.WEAPON_4] = this.prefab_items[6];
        this._itemPrefabsMapping[GameMapItemType.KEY_1] = this.prefab_items[7];
        this._itemPrefabsMapping[GameMapItemType.KEY_2] = this.prefab_items[8];
        this._itemPrefabsMapping[GameMapItemType.ITEM_1] = this.prefab_items[9];        //红药水
        this._itemPrefabsMapping[GameMapItemType.ITEM_2] = this.prefab_items[10];        //蓝药水
        this._itemPrefabsMapping[GameMapItemType.ITEM_3] = this.prefab_items[11];       //勋章
        this._itemPrefabsMapping[GameMapItemType.ITEM_4] = this.prefab_items[12];       //蓝宝石
        this._itemPrefabsMapping[GameMapItemType.ITEM_5] = this.prefab_items[13];       //红宝石
        this._itemPrefabsMapping[GameMapItemType.MONSTER_1] = this.prefab_items[14];    //绿史莱姆
        this._itemPrefabsMapping[GameMapItemType.MONSTER_2] = this.prefab_items[15];    //红史莱姆
        this._itemPrefabsMapping[GameMapItemType.MONSTER_3] = this.prefab_items[16];    //蝙蝠
        this._itemPrefabsMapping[GameMapItemType.MONSTER_4] = this.prefab_items[17];    //骷髅1
        this._itemPrefabsMapping[GameMapItemType.MONSTER_5] = this.prefab_items[18];    //骷髅2
        this._itemPrefabsMapping[GameMapItemType.MONSTER_6] = this.prefab_items[19];    //蓝祭师
        this._itemPrefabsMapping[GameMapItemType.MONSTER_7] = this.prefab_items[20];    //红祭师
        this._itemPrefabsMapping[GameMapItemType.MONSTER_8] = this.prefab_items[21];    //蓝巨人
        this._itemPrefabsMapping[GameMapItemType.MONSTER_9] = this.prefab_items[22];    //红巨人

        this._itemPrefabsMapping[GameMapItemType.BOSS_1] = this.prefab_items[23];       //BOSS
    }

    public async init() {
        //初始化砖块
        await this.refresh(false);

        cc.tween(this.node)
        .repeatForever(
            cc.tween(this.node)
            .by(5, {y: 20})
            .by(5, {y: -20})
        )
        .start();

        //初始化人物
        let coord = GameRoleModel.getInstance().curPos;
        this.roleWidget.init(this.coord2Pos(coord.x, coord.y));
    }

    public exit(){
        this.roleWidget.exit();
        //移出
        return new Promise((resolve, reject)=>{
            let runAnim = function(node: cc.Node, pos: cc.Vec2, delay: number, isEnd = false){
                cc.tween(node)
                .delay(delay)
                .by(0.5, {y: -2000})
                .call(isEnd? resolve : null)
                .start();
            }

            let brickNodes = this._brickNodes;
            let itemNodes = this._itemNodes;
            for(let i = 0; i < brickNodes.length; i++){
                for(let j = 0; j < brickNodes[i].length; j++){
                    let node = brickNodes[i][j];
                    if(!node) continue;

                    let pos = this.coord2Pos(i, j);
                    let isEnd = i == 0 && j == 0;
                    let delay = (brickNodes[i].length - 1 - j) * 0.016 + (brickNodes.length - 1 - i) * 0.1;

                    runAnim(node, pos, delay, isEnd);

                    node = itemNodes[i][j];
                    runAnim(node, pos, delay, isEnd);
                }
            }
        });
    }

    public async refresh(onlyChange: boolean){
        let mapData = GameMapModel.getInstance().data;

        if(onlyChange){
            //刷新变化的
            for(let i = 0; i < mapData.length; i++){
                for(let j = 0; j < mapData[i].length; j++){
                    let item = mapData[i][j];
                    let itemNode = this._itemNodes[i][j];

                    if(!itemNode || !itemNode.active) continue;

                    if(itemNode.getComponent(GameMapItemWidget).itemType == item) continue;

                    if(item == GameMapItemType.NONE || item == GameMapItemType.BOSS_RANGE){
                        itemNode.active = false;
                    }else if(item == GameMapItemType.DOOR_OPEN){
                        itemNode.getComponent(GameMapItemDoorWidget).switchState(item);
                    } 
                }
            }
        }else{
            //清理旧的
            this.brickLayer.removeAllChildren();
            this.itemLayer.removeAllChildren();
            
            this._brickNodes.length = 0;
            this._itemNodes.length = 0;

            let size = cc.size(mapData[0].length * this._itemWidth, mapData.length * this._itemHeight);
            this.node.setContentSize(size);
            //根据canvas调整scale
            this._updateScale();

            //刷新全部
            return new Promise((resolve, reject)=>{
                let runAnim = function(node: cc.Node, pos: cc.Vec2, delay: number, isEnd = false){
                    cc.tween(node)
                    .set({x: pos.x, y: -2000, opacity: 0})
                    .delay(delay)
                    .set({opacity: 255})
                    .to(0.5, {x: pos.x, y: pos.y})
                    .call(isEnd? resolve : null)
                    .start();
                }
                for(let i = 0; i < mapData.length; i++){
                    for(let j = 0; j < mapData[i].length; j++){
                        let item = mapData[i][j];
                        let pos = this.coord2Pos(i, j);
                        let isEnd = i == mapData.length - 1 && j == mapData[i].length - 1;
                        let delay = j * 0.016 + i * 0.1;
                        let brickNode: cc.Node;
                        if((i == 0 && j == 0) || (i == 0 && j == mapData[i].length - 1) || (j == 0 && i == mapData.length - 1) || (i == mapData.length - 1 && j == mapData[i].length - 1)){
                            brickNode = cc.instantiate(this.prefab_brick_conner);
                            if(i == 0 && j == mapData[i].length - 1){
                                brickNode.angle = -90;
                            }else if(j == 0 && i == mapData.length - 1){
                                brickNode.angle = 90;
                            }else if(i == mapData.length - 1 && j == mapData[i].length - 1){
                                brickNode.angle = 180;
                            }
                        }else if(i == 0 || i == mapData.length - 1 || j == 0 || j == mapData[i].length - 1){
                            brickNode = cc.instantiate(this.prefab_brick_frame);
                            if(i == 0){
                                brickNode.angle = -90;
                            }else if(i == mapData.length - 1){
                                brickNode.angle = 90;
                            }else if(j == mapData[i].length - 1){
                                brickNode.angle = 180;
                            }
                        }else{
                            brickNode = cc.instantiate(this.prefab_brick);
                        }
                        brickNode.setParent(this.brickLayer);
                        if(this._brickNodes[i] == null){
                            this._brickNodes[i] = [];
                        }
                        this._brickNodes[i][j] = brickNode;
                        
                        runAnim(brickNode, pos, delay, isEnd);
    
                        if(item == GameMapItemType.NONE || item == GameMapItemType.BOSS_RANGE) continue;
    
                        let itemNode = cc.instantiate(this._itemPrefabsMapping[item]);
    
                        itemNode.setParent(this.itemLayer);
                        itemNode.getComponent(GameMapItemWidget).initWithItem(item);
    
                        if(this._itemNodes[i] == null){
                            this._itemNodes[i] = [];
                        }
                        this._itemNodes[i][j] = itemNode;

                        runAnim(itemNode, pos, delay, isEnd);
                    }
                }
            });
            
        }
    }

    public async monsterAction(actionName: string, params?: any){
        let coord = params[0];
        let itemNode = this._itemNodes[coord.x][coord.y];
        if(!itemNode || !itemNode.getComponent(GameMapItemMonsterWidget)) return;

        if(actionName == "idle"){
            return itemNode.getComponent(GameMapItemMonsterWidget).runIdle();
        }else if(actionName == "attack"){
            let dir = params[1];
            let dest = params[2];
            await itemNode.getComponent(GameMapItemMonsterWidget).runAttack(dir);
            this.genAttackEffect(dest);
        }else if(actionName == "dead"){
            await itemNode.getComponent(GameMapItemMonsterWidget).runDead();
            this.genDeadEffect(coord);
        }
    }

    public roleStop(){
        return this.roleWidget.runStop();
    }

    public roleMove(dir: cc.Vec2){
        let coord = GameRoleModel.getInstance().curPos;
        
        return this.roleWidget.runMove(this.coord2Pos(coord.x, coord.y), dir);
    }

    public async roleAction(actionName: string, params?: any){
        if(actionName == "attack"){
            let dir = params[0];
            let dest = params[1];
            await this.roleWidget.runAttack(dir);
            this.genAttackEffect(dest);
        }else if(actionName == "dead"){
            let dest = params[0];
            await this.roleWidget.runDead();
            this.genDeadEffect(dest);
        }
    }

    public roleUpdate(){
        this.roleWidget.updateProp(true);
    }

    private genAttackEffect(coord: cc.Vec2){
        let node = cc.instantiate(this.prefab_effect_attack);
        node.setParent(this.effectLayer);
        node.setPosition(this.coord2Pos(coord.x, coord.y));
        node.getComponent(cc.Animation).once(cc.Animation.EventType.FINISHED, ()=>{
            node.destroy();
        });
        node.getComponent(cc.Animation).play();
    }

    private genDeadEffect(coord: cc.Vec2){
        let node = cc.instantiate(this.prefab_effect_dead);
        node.setParent(this.effectLayer);
        node.setPosition(this.coord2Pos(coord.x, coord.y));
        node.getComponent(cc.Animation).once(cc.Animation.EventType.FINISHED, ()=>{
            node.destroy();
        });
        node.getComponent(cc.Animation).play();
    }

    public coord2Pos(row: number, col: number): cc.Vec2{
        return cc.v2((col + 0.5) * this._itemWidth, ((row + 0.5) * -this._itemHeight));
    }

    public pos2Coord(worldPos: cc.Vec2): cc.Vec2{
        let pos = this.node.convertToNodeSpaceAR(worldPos);
        return cc.v2(Math.floor(Math.abs(pos.y / this._itemHeight)), Math.floor(pos.x / this._itemWidth));
    }

    protected onCanvasSizeChanged(): void {
        this._updateScale();
    }

    private _updateScale(){
        let size = this.node.getContentSize();
        let viewSize = cc.view.getVisibleSize();
        let widget = this.node.getComponent(cc.Widget);

        if(viewSize.width - size.width < viewSize.height - size.height){
            let scale = (viewSize.width - 20) / size.width;
            this.node.setScale(scale);
            widget.verticalCenter = 50;
        }else{
            let scale = (viewSize.height - 40) / size.height;
            this.node.setScale(scale);
            widget.verticalCenter = 0;
        }

        this.node.getComponent(cc.Widget).updateAlignment();
    }
}